import { URI } from "vscode-uri";
import { resolve } from "path";
import { t, util } from "./analysis";
import { CompletionItemKind, DiagnosticSeverity, SymbolKind } from "vscode-languageserver";
import { longCodeTextToMetadataLanguage, shortCodeTextToMetadataLanguage } from "../common/languages";
import {
    DefaultOptions,
    DsProperties,
    DsWindowProperties,
    DsWindowPropertiesUpdateParams,
    LineFeedCharater,
    LocalLanguage,
    MDMLoaderOptions,
    MDMSerializerOptions,
    ScriptOptions,
    ScriptOptionUpdateParam,
    _defaultLanguage
} from "../common/types";
import { DiagnosticFlag } from "./diagnostic";

export function uriToPath(documnetUri: string): string {
    return URI.parse(documnetUri).fsPath;
}

export function pathToUri(fsPath: string) {
    return URI.file(fsPath).toString();
}

export function normalizeFileNameToFsPath(fileName: string) {
    return URI.file(fileName).fsPath;
}

export function normalizeFileNameResolve(...paths: string[]) {
    return normalizeFileNameToFsPath(resolve(...paths));
}

const symbolFlagToCompletionItemKindObject: [t.SymbolKind, CompletionItemKind][] = [
    [t.SymbolKind.variable | t.SymbolKind.array | t.SymbolKind.parameter, CompletionItemKind.Variable],
    [t.SymbolKind.constant | t.SymbolKind.macro, CompletionItemKind.Constant],
    [t.SymbolKind.function | t.SymbolKind.macroFunction, CompletionItemKind.Function],
    [t.SymbolKind.method, CompletionItemKind.Method],
    [t.SymbolKind.property, CompletionItemKind.Property],
    [t.SymbolKind.class, CompletionItemKind.Class],
    [t.SymbolKind.interface, CompletionItemKind.Interface],
    [t.SymbolKind.enum, CompletionItemKind.Enum],
    [t.SymbolKind.enumMember, CompletionItemKind.EnumMember],
    [t.SymbolKind.module, CompletionItemKind.Module],
];

const metadataFlagToCompletionItemKindObject: [t.SymbolMDMKind, CompletionItemKind][] = [
    [
        t.SymbolMDMKind.areaName |
        t.SymbolMDMKind.categories |
        t.SymbolMDMKind.field,
        CompletionItemKind.Field
    ],
    [
        t.SymbolMDMKind.property |
        t.SymbolMDMKind.cellStyle |
        t.SymbolMDMKind.fontStyle |
        t.SymbolMDMKind.controlStyle |
        t.SymbolMDMKind.style,
        CompletionItemKind.Property
    ],
    [t.SymbolMDMKind.element, CompletionItemKind.EnumMember],
];

export function symbolFlagToCompletionItemKind(flag: t.SymbolKind): CompletionItemKind {
    for (const item of symbolFlagToCompletionItemKindObject) {
        if ((flag & item[0]) > 0) {
            return item[1];
        }
    }
    return CompletionItemKind.Text;
}

export function metadataFlagToCompletionItemKind(flag: t.SymbolMDMKind): CompletionItemKind {
    for (const item of metadataFlagToCompletionItemKindObject) {
        if ((flag & item[0]) > 0) {
            return item[1];
        }
    }
    return CompletionItemKind.Text;
}

export function syntaxFlagToDocumentSymbolKind(flag: t.SyntaxKind, maybeMethod: boolean = false): SymbolKind {
    switch (flag) {
        case t.SyntaxKind.moduleSignature:               return SymbolKind.Namespace;
        case t.SyntaxKind.interfaceSignature:            return SymbolKind.Interface;
        case t.SyntaxKind.classSignature:                return SymbolKind.Class;
        case t.SyntaxKind.functionSignature:             return maybeMethod ? SymbolKind.Method : SymbolKind.Function;
        case t.SyntaxKind.propertySignature:             return SymbolKind.Property;
        case t.SyntaxKind.enumSignature:                 return SymbolKind.Enum;
        case t.SyntaxKind.enumMemberSignature:           return SymbolKind.EnumMember;
        case t.SyntaxKind.funcDecl:           return SymbolKind.Function;
        case t.SyntaxKind.sectionStat:              return SymbolKind.Field;
        case t.SyntaxKind.eventSection:                  return SymbolKind.Event;
        case t.SyntaxKind.ppDefine:   return SymbolKind.Constant;
        case t.SyntaxKind.varDecl:           return SymbolKind.Variable;
        case t.SyntaxKind.arrayDecl:              return SymbolKind.Array;
        case t.SyntaxKind.constDecl:           return SymbolKind.Constant;
        case t.SyntaxKind.mdmField:                 return SymbolKind.Field;
        case t.SyntaxKind.parameterSignature:            return SymbolKind.Variable;
        case t.SyntaxKind.inputDataSourceSection:
        case t.SyntaxKind.outputDataSourceSection:
        case t.SyntaxKind.loggingSection:
        case t.SyntaxKind.metadataSection:
            return SymbolKind.Object;
        case t.SyntaxKind.mdmCategory:
            return SymbolKind.EnumMember;
    }
    return SymbolKind.Null;
}

export function stringToLineFeedCharater(str: string): LineFeedCharater {
    const upper = str.toUpperCase().trim();
    return upper === "LF" ? "\n" : "\r\n";
}

export function lineFeedCharaterToString(str: LineFeedCharater): string {
    return str === "\n" ? "LF" : "CRLF";
}

export function diagnosticFlagToDiagnosticSeverity(flag: DiagnosticFlag) {
    switch (flag) {
        case DiagnosticFlag.error:      return DiagnosticSeverity.Error;
        case DiagnosticFlag.warning:    return DiagnosticSeverity.Warning;
        case DiagnosticFlag.suggestion: return DiagnosticSeverity.Information;
        case DiagnosticFlag.message:    return DiagnosticSeverity.Hint;
    }
}

export function dsWindowPropertiesUpdateParamsToScriptOptionUpdateParam(param: DsWindowPropertiesUpdateParams): ScriptOptionUpdateParam {
    let result: ScriptOptionUpdateParam = {};
    if (param.userName !== undefined) {
        result.userName = param.userName;
    }
    let context: t.MDMUserContextFlags | undefined;
    if (param.metadataContext && (context = util.stringToUserContext(param.metadataContext)) !== undefined) {
        result.defaultMetadataContext = context;
    }
    let labelType: t.MDMLabelTypeFlags | undefined;
    if (param.metadataLabelType && (labelType = util.stringToLabelType(param.metadataLabelType)) !== undefined) {
        result.defaultMetadataLabelType = labelType;
    }
    let language: LocalLanguage | undefined;
    if (param.metadataLanguage && (language = longCodeTextToMetadataLanguage(param.metadataLanguage))) {
        result.defaultMetadataLanguage = language;
    }
    return result;
}

export function dsConfigurationToScriptOptions(config: DsWindowProperties): ScriptOptions {
    return {
        userName: config.userName,
        defaultMetadataContext: util.stringToUserContext(config.metadataContext ?? DefaultOptions.metadataContext) ?? t.MDMUserContextFlags.analysis,
        defaultMetadataLanguage: longCodeTextToMetadataLanguage(config.metadataLanguage) ?? _defaultLanguage,
        defaultMetadataLabelType: util.stringToLabelType(config.metadataLabelType ?? DefaultOptions.metadataLabelType) ?? t.MDMLabelTypeFlags.label,
    };
}

export function dsConfigurationToMDMSerializerOptions(config: DsProperties): MDMSerializerOptions {
    return {
        indentSpaceCount: config["mdmSerializer.indentSpaceCount"] ?? DefaultOptions.mdmSerializerOptions.indentSpaceCount!,
        lineFeedCharater: config["mdmSerializer.lineFeedCharater"] ? stringToLineFeedCharater(config["mdmSerializer.lineFeedCharater"]) : DefaultOptions.mdmSerializerOptions.lineFeedCharater,
        invalidValueLowerBoundary: config["mdmSerializer.invalidValueLowerBoundary"] ?? DefaultOptions.mdmSerializerOptions.invalidValueLowerBoundary,
        invalidValueUpperBoundary: config["mdmSerializer.invalidValueUpperBoundary"] ?? DefaultOptions.mdmSerializerOptions.invalidValueUpperBoundary,
        hideLabeledObjectProperties: config["mdmSerializer.hideLabeledObjectProperties"] ?? DefaultOptions.mdmSerializerOptions.hideLabeledObjectProperties,
    };
}

export function dsConfigurationToMDMLoaderOptions(config: DsProperties): MDMLoaderOptions {
    return {
        mdmVersion: config["mdmLoader.mdmVersion"] ?? DefaultOptions.mdmLoaderOptions.mdmVersion,
        invalidValueLowerBoundary: config["mdmLoader.invalidValueLowerBoundary"] ?? DefaultOptions.mdmLoaderOptions.invalidValueLowerBoundary,
        invalidValueUpperBoundary: config["mdmLoader.invalidValueUpperBoundary"] ?? DefaultOptions.mdmLoaderOptions.invalidValueUpperBoundary,
        defaultContext: config["mdmLoader.defaultContext"] ?? DefaultOptions.mdmLoaderOptions.defaultContext,
        defaultLabelType: config["mdmLoader.defaultLabelType"] ?? DefaultOptions.mdmLoaderOptions.defaultLabelType,
        defaultLanguage: (shortCodeTextToMetadataLanguage(config["mdmLoader.defaultLanguage"]) || longCodeTextToMetadataLanguage(config["mdmLoader.defaultLanguage"])) ?? DefaultOptions.mdmLoaderOptions.defaultLanguage,
        defaultRoutingContext: config["mdmLoader.defaultRoutingContext"] ?? DefaultOptions.mdmLoaderOptions.defaultRoutingContext,
        defaultScriptType: config["mdmLoader.defaultScriptType"] ?? DefaultOptions.mdmLoaderOptions.defaultScriptType,
        checkAliasConflict: config["mdmLoader.checkAliasConflict"] !== undefined ? config["mdmLoader.checkAliasConflict"] : DefaultOptions.mdmLoaderOptions.checkAliasConflict,
        userName: config.userName ?? DefaultOptions.scriptOptions.userName,
    };
}
